<?php

/**
 * @copyright Michiel Hakvoort 2010
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD
 * @package mangrove
 * @subpackage core
 * @filesource
 */

/*
 * Copyright (c) 2010 Michiel Hakvoort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

namespace mg {


    interface L10N {
        public function localeChanged(Locale $locale);
    }

    interface I18N {
        public function charsetChanged($charset);
    }

    class G11N {

        /**
         * @var Locale
         */
        private $locale = null;

        private $charset = null;

        private $l10nListeners = array();
        private $i18nListeners = array();

        public function __construct($charset = null, $locale = null) {
            if($charset === null) {
                $charset = mb_internal_encoding();
            }

            if($locale === null) {
                $defaultLocale = @ini_get('intl.default_locale') ?: 'en_US';
                $locale = Locale :: getLocale($defaultLocale);
            }

            $this->locale = $locale;
            $this->charset = $charset;
        }

        public function addL10NListener(L10N $listener) {
            $this->l10nListeners[] = $listener;
        }

        public function addI18NListener(I18N $listener) {
            $this->i18nListeners[] = $listener;
        }

        public function setLocale(Locale $locale) {
            $this->locale = $locale;

            /* @var $listener L10N */
            foreach($this->l10nListeners as $listener) {
                $listener->localeChanged($this->locale);
            }
        }

        /**
         *
         * @return Locale
         */
        public function getLocale() {
            return $this->locale;
        }

        public function setCharset($charset) {
            $this->charset = $charset;

            /* @var $listener I18N */
            foreach($this->i18nListeners as $listener) {
                $listener->charsetChanged($this->charset);
            }
        }

        public function getCharset() {
            return $this->charset;
        }

        /**
         * Utility function for temporarily modifying the Locale
         *
         * @param Locale $locale
         * @param Closure $closure
         * @return result of $closure
         */
        public static function inLocale(Locale $locale, \Closure $closure) {
            /* @var $g11n G11N */
            $g11n = get('mg\G11N');
            $previousLocale = $g11n->getLocale();
            $g11n->setLocale($locale);
            $e = null;

            try {
                $result = $closure();
            } catch(\Exception $e) {

            }

            $g11n->setLocale($previousLocale);
            	
            if($e) {
                throw $e;
            }

            return $result;
        }
    }

    /**
     *
     * @author Michiel Hakvoort
     *
     */
    class Locale {

        private static $locales = array();

        private $language = null;
        private $script = null;
        private $region = null;
        private $variants = null;

        private $canonicalLocale = null;

        public function __construct($language, $script = null, $region = null, array $variants = array()) {
            $this->language = $language;
            $this->script = $script;
            $this->region = $region;
            $this->variants = $variants;
        }

        public function getLanguage() {
            return $this->language;
        }

        public function getScript() {
            return $this->script;
        }

        public function getRegion() {
            return $this->region;
        }

        public function getVariants() {
            return $this->variants;
        }

        /**
         *
         * @param string $locale
         * @return \mg\Locale
         */
        public static function getLocale($locale) {
            $elements = \Locale :: parseLocale($locale);

            $canonicalLocale = \Locale :: composeLocale($elements);

            if(isset(self :: $locales[$canonicalLocale])) {
                return self :: $locales[$canonicalLocale];
            }

            $result = new Locale(isset($elements['language']) ? $elements['language'] : null
            ,isset($elements['script']) ? $elements['script'] : null
            ,isset($elements['region']) ? $elements['region'] : null
            ,\Locale :: getAllVariants($locale));

            self :: $locales[$canonicalLocale] = $result;

            $result->canonicalLocale = $canonicalLocale;
            	
            return $result;
        }

        public function __toString() {
            if($this->canonicalLocale !== null) {
                return $this->canonicalLocale;
            }

            $canonicalLocale = $this->language;

            if($this->script !== null) {
                $canonicalLocale .= "_{$this->script}";
            }

            if($this->region !== null) {
                $canonicalLocale .= "_{$this->region}";
            }

            if(!empty($this->variants)) {
                $canonicalLocale .= '_' . implode('_', $this->variants);
            }

            $this->canonicalLocale = $canonicalLocale;

            return $canonicalLocale;
        }
    }

    class Charsets {

        const ISO_8859_1	= 'ISO-8859-1';
        const US_ASCII		= 'US-ASCII';
        const UTF_8			= 'UTF-8';

        private function __construct() {
            	
        }
    }

    class ParameterizedMessage {

        public $message = null;
        public $arguments = null;

        public function __construct($message, array $arguments = array()) {
            $this->message = $message;
            $this->arguments = $arguments;
        }

        public function getMessage() {
            return $this->message;
        }

        public function getArguments() {
            return $this->arguments;
        }

    }

}

namespace {

    function message($message) {
        $arguments = func_get_args();
        array_shift($arguments);

        return new \mg\ParameterizedMessage($message, $arguments);
    }

    function g11n($message) {
        $arguments = func_get_args();
        array_shift($arguments);

        return new \mg\ParameterizedMessage($message, $arguments);
    }

}
